﻿using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using AxSHDocVw; // Use `aximp %SystemRoot%\system32\shdocvw.dll`
using IECaptComImports;
using System.IO;
using System.Windows.Forms;

namespace TypedROBIN.Imaging
{
    public class IECapt
    {
        public delegate void CompleteEventHandler(object sender, CompleteEventArgs e);
        public delegate void ErrorEventHandler(object sender, ErrorEventArgs e);
        public delegate void StatusEventHandler(object sender, StatusEventArgs e);
        public delegate void TarceEventHandler(object sender, StatusEventArgs e);
        public event CompleteEventHandler OnComplete;
        public event ErrorEventHandler OnError;
        public event StatusEventHandler OnStatus;
        public event TarceEventHandler OnTarce;
        private System.Timers.Timer timeout;
        private bool returnFlag = false;
        private IECaptForm ieCaptForm;

        private void ReportStatus(string status)
        {
            StatusEventArgs sea = new StatusEventArgs(status);
            OnStatus(this, sea);
            WriteTrace(status);
        }

        private void WriteTrace(string traceInfo)
        {
            StatusEventArgs sea = new StatusEventArgs(traceInfo);
            OnTarce(this, sea);
        }

        private void DisposeIECaptForm(IECaptForm form)
        {
            try
            {
                form.mWb.Dispose();
                form.Close();
                form.Dispose();
                Application.ExitThread();
            }
            catch
            {
            }
        }

        public void Execute(
            string url,
            string output,
            int minWidth = 1024,
            int maxWait = 90000,
            int delay = 1000)
        {
            WriteTrace("Begin IECapt.Execute");
            if (delay < 1000 || url == null || output == null)
            {
                ErrorEventArgs eea = new ErrorEventArgs(
                    (string)url,
                    777,
                    1);
                OnError(this, eea);
                WriteTrace("error: delay < 1000 或 url == null 或 output == null");
                return;
            }

            returnFlag = false;
            timeout = new System.Timers.Timer();
            timeout.Interval = maxWait;
            timeout.Elapsed += new System.Timers.ElapsedEventHandler(timeout_Elapsed);
            timeout.Enabled = true;
            timeout.Start();
            ReportStatus("Waiting " + maxWait/1000 + " seconds for page to load.");

            AxWebBrowser wb = new AxWebBrowser();
            ieCaptForm = new IECaptForm(url, output, minWidth, delay, wb);

            wb.BeginInit();
            wb.Parent = ieCaptForm;
            wb.EndInit();

            // Set the initial dimensions of the browser's client area.
            wb.SetBounds(0, 0, minWidth, 600);

            object oBlank = "about:blank";
            object oURL = url;
            object oNull = String.Empty;

            // Internet Explorer should show no dialog boxes; this does not dis-
            // able script debugging however, I am not aware of a method to dis-
            // able that, other than manual configuration in he Internet Settings
            // or perhaps the registry.
            wb.Silent = true;
            // The custom UI handler can only be registered on a document, so we
            // navigate to about:blank as a first step, then register the handler.
            wb.Navigate2(ref oBlank, ref oNull, ref oNull, ref oNull, ref oNull);

            ICustomDoc cdoc = wb.Document as ICustomDoc;
            cdoc.SetUIHandler(new IECaptUIHandler());

            // Register a document complete handler. It will be called whenever a
            // document completes loading, including embedded documents and the
            // initial about:blank document.
            wb.DocumentComplete +=
              new DWebBrowserEvents2_DocumentCompleteEventHandler(IE_DocumentComplete);

            // Register an error handler. If the main document cannot be loaded,
            // the document complete event will not fire, so we have to listen to
            // this and shut the application down in case of a fatal error.
            wb.NavigateError +=
              new DWebBrowserEvents2_NavigateErrorEventHandler(IE_NavigateError);

            //ieCaptForm.Width = minWidth;
            //ieCaptForm.Height = 200;
            //ieCaptForm.Show();

            // Now navigate to the final destination.
            wb.Navigate2(ref oURL, ref oNull, ref oNull, ref oNull, ref oNull);
            Application.Run();
        }

        void timeout_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            timeout.Enabled = false;
            if (!returnFlag)
            {
                returnFlag = true;
                ReportStatus("Timeout, but document still uncompleted. Anyway, we are trying to capture this document now.");
                try
                {
                    ieCaptForm.DoCapture();
                    long executionTick = (DateTime.Now.Ticks - ieCaptForm.mStartTicks) / 10000;
                    CompleteEventArgs cea = new CompleteEventArgs(
                        ieCaptForm.mURL,
                        ieCaptForm.mFile,
                        new FileInfo(ieCaptForm.mFile).Length,
                        ieCaptForm.mDelay,
                        executionTick);
                    OnComplete(this, cea);
                }
                catch (Exception ex)
                {
                    WriteTrace("Error when force DoCapture: " + ex.Message);
                    throw;
                }
                finally
                {
                    DisposeIECaptForm(ieCaptForm);
                }
            }
        }

        private void IE_DocumentComplete(object sender,
            DWebBrowserEvents2_DocumentCompleteEvent e)
        {
            WriteTrace("Event triggered: document load completed.");

            AxWebBrowser wb = (AxWebBrowser)sender;
            IECaptForm main = (IECaptForm)wb.Parent;

             // Skip document complete event for embedded frames.
            if (wb.Application != e.pDisp)
            {
                WriteTrace("Event abandoned: not target document loaded, it's embedded frames.");
                return;
            }

            // Skip the initial about:blank document; this is not necessarily
            // the best thing to do, e.g. if the requested page is about:blank
            // or redirects to it, we might never exit. This could be avoided
            // by remembering whether we saw the first document complete event.
            if (e.uRL.Equals("about:blank"))
            {
                WriteTrace("Event abandoned: not target document loaded, it's blank page.");
                return;
            }

            returnFlag = true;

            System.Threading.Thread.Sleep(main.mDelay);
            ReportStatus("Waiting " + main.mDelay / 1000 + " seconds for document to load.");
            try
            {
                main.DoCapture();
                long executionTick = (DateTime.Now.Ticks - main.mStartTicks) / 10000;
                CompleteEventArgs cea = new CompleteEventArgs(
                    main.mURL,
                    main.mFile,
                    new FileInfo(main.mFile).Length,
                    main.mDelay,
                    executionTick);
                OnComplete(this, cea);
            }
            catch (Exception ex)
            {
                WriteTrace("Error when DoCapture: " + ex.Message);
                throw;
            }
            finally
            {
                DisposeIECaptForm(main);
            }

        }

        private void IE_NavigateError(object sender,
            DWebBrowserEvents2_NavigateErrorEvent e)
        {
            returnFlag = true;
            WriteTrace("Event triggered: document load error.");

            AxWebBrowser wb = (AxWebBrowser)sender;
            IECaptForm main = (IECaptForm)wb.Parent;

            // Ignore errors for embedded documents
            if (wb.Application != e.pDisp)
            {
                WriteTrace("Event abandoned: not target document error, it's embedded frames.");
                return;
            }
            // If we get here, the main document cannot be navigated 
            // to meaning there is nothing to draw, so we just croak.
            //Console.Error.WriteLine("Failed to navigate to {0} (0x{1:X08})",
            //  e.uRL, e.statusCode);

            DisposeIECaptForm(main);

            long executionTicks = (DateTime.Now.Ticks - main.mStartTicks) / 10000;
            ErrorEventArgs eea = new ErrorEventArgs(
                (string)e.uRL,
                (int)e.statusCode,
                executionTicks);
            OnError(this, eea);
        }
    }
}
